#include "base64.h"

uint8_t *base64_encode(uint8_t *base64_str, uint32_t base64_str_len)
{
    uint32_t len;
    uint32_t str_len;
    uint8_t *res;
    uint32_t i, j;
    //定义base64编码表
    uint8_t *base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    //计算经过base64编码后的字符串长度
    str_len = base64_str_len;
    if (str_len % 3 == 0)
        len = str_len / 3 * 4;
    else
        len = (str_len / 3 + 1) * 4;

    res = malloc(sizeof(uint8_t) * len + 1);
    res[len] = '\0';

    //以3个8位字符为一组进行编码
    for (i = 0, j = 0; i < len - 2; j += 3, i += 4)
    {
        res[i] = base64_table[base64_str[j] >> 2];                                            //取出第一个字符的前6位并找出对应的结果字符
        res[i + 1] = base64_table[(base64_str[j] & 0x3) << 4 | (base64_str[j + 1] >> 4)];     //将第一个字符的后位与第二个字符的前4位进行组合并找到对应的结果字符
        res[i + 2] = base64_table[(base64_str[j + 1] & 0xf) << 2 | (base64_str[j + 2] >> 6)]; //将第二个字符的后4位与第三个字符的前2位组合并找出对应的结果字符
        res[i + 3] = base64_table[base64_str[j + 2] & 0x3f];                                  //取出第三个字符的后6位并找出结果字符
    }

    switch (str_len % 3)
    {
    case 1:
        res[i - 2] = '=';
        res[i - 1] = '=';
        break;
    case 2:
        res[i - 1] = '=';
        break;
    }

    return res;
}

// uint8_t *base64_decode(uint8_t *code, uint32_t *dataLen)
// {
//     //根据base64表，以字符找到对应的十进制数据
//     uint8_t table[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
//                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
//                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
//                        0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0,
//                        63, 52, 53, 54, 55, 56, 57, 58,
//                        59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0,
//                        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
//                        13, 14, 15, 16, 17, 18, 19, 20, 21,
//                        22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26,
//                        27, 28, 29, 30, 31, 32, 33, 34, 35,
//                        36, 37, 38, 39, 40, 41, 42, 43, 44,
//                        45, 46, 47, 48, 49, 50, 51};
//     uint32_t len;
//     uint32_t str_len;
//     uint8_t *res;
//     uint32_t i, j;

//     //计算解码后的字符串长度
//     len = strlen(code);
//     //判断编码后的字符串后是否有=
//     if (strstr(code, "=="))
//     {
//         str_len = len / 4 * 3 - 2;
//     }
//     else if (strstr(code, "="))
//     {
//         str_len = len / 4 * 3 - 1;
//     }
//     else
//     {
//         str_len = len / 4 * 3;
//     }

//     // TODO：检查下图片是不是有点问题，因为+3，末尾会不会是全是00，虽然图片好像没什么影响
//     // res = malloc(sizeof(uint8_t) * str_len + 3);
//     // memset(res, 0, str_len + 3);
//     // *dataLen = str_len + 3;

//     res = malloc(sizeof(uint8_t) * str_len);
//     memset(res, 0, str_len);
//     *dataLen = str_len;

//     //以4个字符为一位进行解码
//     for (i = 0, j = 0; i < len - 2; j += 3, i += 4)
//     {
//         if (j + 2 >= str_len)
//         {
//             str_len += j + 2 + 1 - str_len;
//             uint8_t *reRes = realloc(res, str_len);
//             if (reRes == NULL)
//             {
//                 LOG_ERROR("重新申请内存失败");
//                 abort();
//             }
//             res = reRes;
//             *dataLen += 1;
//         }
//         res[j] = ((uint8_t)table[code[i]]) << 2 | (((uint8_t)table[code[i + 1]]) >> 4);           //取出第一个字符对应base64表的十进制数的前6位与第二个字符对应base64表的十进制数的后2位进行组合
//         res[j + 1] = (((uint8_t)table[code[i + 1]]) << 4) | (((uint8_t)table[code[i + 2]]) >> 2); //取出第二个字符对应base64表的十进制数的后4位与第三个字符对应bas464表的十进制数的后4位进行组合
//         res[j + 2] = (((uint8_t)table[code[i + 2]]) << 6) | ((uint8_t)table[code[i + 3]]);        //取出第三个字符对应base64表的十进制数的后2位与第4个字符进行组合
//     }

//     return res;
// }

const uint8_t base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
/**
 *找到ch在base中的位置
 */
static uint8_t find_pos(uint8_t ch)
{
    //the last position (the only) in base[]
    uint8_t *ptr = (uint8_t *)strrchr(base, ch);
    return (ptr - base);
}

uint8_t *base64_decode(uint8_t *data, uint32_t *len)
{
    uint32_t data_len = strlen(data);
    uint32_t ret_len = (data_len / 4) * 3 + 1;
    uint32_t equal_count = 0;
    uint8_t *ret = NULL;
    uint8_t *f = NULL;
    *len = 0;
    uint32_t tmp = 0;
    uint32_t temp = 0;
    uint8_t need[3];
    uint32_t prepare = 0;
    uint32_t i = 0;
    if (*(data + data_len - 1) == '=')
    {
        equal_count += 1;
    }
    if (*(data + data_len - 2) == '=')
    {
        equal_count += 1;
    }

    ret = (uint8_t *)malloc(ret_len);
    if (ret == NULL)
    {
        printf("No enough memory.n");
        exit(0);
    }
    memset(ret, 0, ret_len);
    f = ret;
    while (tmp < (data_len - equal_count))
    {
        temp = 0;
        prepare = 0;
        memset(need, 0, 4);
        while (temp < 4)
        {
            if (tmp >= (data_len - equal_count))
            {
                break;
            }
            prepare = (prepare << 6) | (find_pos(data[tmp]));
            temp++;
            tmp++;
        }
        prepare = prepare << ((4 - temp) * 6);
        for (i = 0; i < 3; i++)
        {
            if (i == temp)
            {
                break;
            }
            *f = (uint8_t)((prepare >> ((2 - i) * 8)) & 0xFF);
            f++;
            (*len)++;
        }
    }
    if (data[data_len - 1] == '=')
    {
        (*len)--;
    }

    return ret;
}